﻿using System.Reflection;
using UnityEngine;
using System.Collections.Generic;

namespace ProjectTW.Foliage
{
    public static class FoliageStandaloneUtility
    {
        private static System.Action<Plane[], Matrix4x4> s_ExtractPlanes;
        
        private static System.Action<Plane[], Matrix4x4> ExtractPlanes
        {
            get
            {
                if (s_ExtractPlanes == null)
                {
                    MethodInfo info = typeof(GeometryUtility).GetMethod("Internal_ExtractPlanes", BindingFlags.Static | BindingFlags.NonPublic, null, new System.Type[] { typeof(Plane[]), typeof(Matrix4x4) }, null);
                    s_ExtractPlanes = System.Delegate.CreateDelegate(typeof(System.Action<Plane[], Matrix4x4>), info) as System.Action<Plane[], Matrix4x4>;
                }

                return s_ExtractPlanes;
            }
        }

        public static Plane[] CalculateFrustumPlanes(Camera camera, Plane[] planeArray)
        {
            ExtractPlanes(planeArray, camera.projectionMatrix * camera.worldToCameraMatrix);
            return planeArray;
        }

        /// <summary>
        /// Try to get the id of an certain detail prototype from the db.
        /// </summary>
        /// <param name="prototype"></param>
        /// <returns></returns>
        public static int TryGetPrototypeIndex(DetailPrototype prototype)
        {
            for (int i = 0; i < FoliageDB.UnSortedPrototypes.Count; i++)
            {
                if (FoliageDB.UnSortedPrototypes[i].EqualsToPrototype(prototype))
                {
                    return FoliageDB.UnSortedPrototypes[i].Id;
                }
            }

            return -1;
        }

        /// <summary>
        /// Get the terrain details on the terrain
        /// </summary>
        /// <returns></returns>
        public static List<int[,]> GetTerrainDetails(TerrainData terrainData)
        {
            List<int[,]> detailPrototypes = new List<int[,]>();

            for (int i = 0; i < terrainData.detailPrototypes.Length; i++)
            {
                detailPrototypes.Add(terrainData.GetDetailLayer(0, 0, terrainData.detailWidth, terrainData.detailHeight, i));
            }

            return detailPrototypes;
        }

        /// <summary>
        /// This will add prototypes to the prototypes cache if they dont already exist.
        /// </summary>
        /// <param name="terrain"></param>
        public static FoliagePrototype[] AddPrototypesIfDontExist(DetailPrototype[] prototypes)
        {
            FoliagePrototype[] foliagePrototypes = new FoliagePrototype[prototypes.Length];

            if (FoliageDB.UnSortedPrototypes.Count == 0) // if there are 0 prototypes => lets create them instantly because they cant be duplicated!.
            {
                for (int i = 0; i < prototypes.Length; i++)
                {
                    foliagePrototypes[i] = FoliageDB.Instance.AddPrototype(prototypes[i]);
                }

                return foliagePrototypes;
            }

            for (int i = 0; i < prototypes.Length; i++)
            {
                for (int b = 0; b < FoliageDB.UnSortedPrototypes.Count; b++)
                {
                    if (FoliageDB.UnSortedPrototypes[b].EqualsToPrototype(prototypes[i]))
                    {
                        foliagePrototypes[i] = FoliageDB.UnSortedPrototypes[b];

                        break;
                    }
                    else if (b == FoliageDB.UnSortedPrototypes.Count - 1) // if we didnt find any match and this is the last index
                    {
                        foliagePrototypes[i] = FoliageDB.Instance.AddPrototype(prototypes[i]);
                    }
                }
            }

            return foliagePrototypes;
        }

        /// <summary>
        /// 检查细节对象9宫格范围。
        /// </summary>
        /// <param name="pos"></param>
        public static FoliageChunk[] GetFoliageChunksNeighbors(Vector3 pos, FoliageChunk[] cache)
        {
            if (cache == null)
            {
                cache = new FoliageChunk[9];
            }

            if (cache.Length != 9)
            {
                Debug.LogError("UNATURE ERROR! CACHE SIZE IS BIGGER THAN NINE!");
                return null;
            }

            FoliageManager manager = FoliageManager.Instance;

            if (manager == null)
            {
                Debug.LogError("UNATURE ERROR! Foliage Manager can't be found!");
                return null;
            }

            int topDownAdjuster = FoliageManager.Foliage_Area_Resolution;
            int rightLeftAdjuster = 1;

            int centerMiddleID = FoliageManager.Instance.GetChunkID(pos.x, pos.z);
            int centerRightID = centerMiddleID - rightLeftAdjuster;
            int centerLeftID = centerMiddleID + rightLeftAdjuster;

            int topMiddleID = centerMiddleID + topDownAdjuster;
            int topRightID = topMiddleID + rightLeftAdjuster;
            int topLeftID = topMiddleID - rightLeftAdjuster;

            int bottomMiddleID = centerMiddleID - topDownAdjuster;
            int bottomRightID = bottomMiddleID + rightLeftAdjuster;
            int bottomLeftID = bottomMiddleID - rightLeftAdjuster;

            cache[0] = manager.CheckChunkInBounds(bottomRightID) ? manager.Sector.FoliageChunks[bottomRightID] : null; // bottom right
            cache[1] = manager.CheckChunkInBounds(bottomMiddleID) ? manager.Sector.FoliageChunks[bottomMiddleID] : null; // bottom center
            cache[2] = manager.CheckChunkInBounds(bottomLeftID) ? manager.Sector.FoliageChunks[bottomLeftID] : null; // bottom left
            cache[3] = manager.CheckChunkInBounds(centerRightID) ? manager.Sector.FoliageChunks[centerRightID] : null; // middle right
            cache[4] = manager.CheckChunkInBounds(centerMiddleID) ? manager.Sector.FoliageChunks[centerMiddleID] : null; // middle center
            cache[5] = manager.CheckChunkInBounds(centerLeftID) ? manager.Sector.FoliageChunks[centerLeftID] : null; // middle left
            cache[6] = manager.CheckChunkInBounds(topRightID) ? manager.Sector.FoliageChunks[topRightID] : null; // top right
            cache[7] = manager.CheckChunkInBounds(topMiddleID) ? manager.Sector.FoliageChunks[topMiddleID] : null; // top center
            cache[8] = manager.CheckChunkInBounds(topLeftID) ? manager.Sector.FoliageChunks[topLeftID] : null; // top left

            return cache;
        }
    }
}
